package com.vanysoft.framework.aop;

import com.vanysoft.business.sys.entity.Api;
import com.vanysoft.business.sys.entity.UserInfo;
import com.vanysoft.framework.exception.VanySoftBaseException;
import com.vanysoft.framework.messagesource.MessageCodeConstant;
import com.vanysoft.framework.redis.RedisConstants;
import com.vanysoft.framework.redis.RedisService;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Objects;

/**
 * 类名称：OnLineAnnotationAspect
 * 类描述：拦截所有的@RequestMapping注解，对其进行权限验证
 * 创建人：SharpClaw_x
 * 创建时间：2017年5月15日 上午11:56:11
 * 修改人：SharpClaw_x
 * 修改时间：2017年5月15日 上午11:56:11
 * 版本信息：@version 1.0
 * Copyright (c) 2017SharpClaw-版权所有
 * 修改备注：
 */
@Component
@Aspect
public class RequestMappingAnnotationAspect {

    /**
     * 完全开放
     */
    public final static String BASE_API_OPEN = "OPEN";
    /**
     * 需要登录才能访问
     */
    public final static String BASE_API_LOGIN = "LOGIN";
    /**
     * 被授权的用户才能访问
     */
    public final static String BASE_API_AUTHORIZED = "AUTHORIZED";
    /**
     * 系统管理员角色才能访问
     */
    public final static String BASE_API_ADMINSTRATORS = "ADMINSTRATORS";
    private static final Logger log = LoggerFactory.getLogger(RequestMappingAnnotationAspect.class);
    @Autowired
    private RedisService redisService;

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    private void cut() {
    }

    /**
     * Function Name               before
     *
     * @param joinPoint
     * @throws VanySoftAspectException
     * @description 切面处理，验证session是否过期
     * Modify History:              Date             Programmer       Notes
     * ---------        ---------------  ---------
     * 2017年5月15日 上午11:58:37           SharpClaw_x                     Initial
     * *********************************************************************
     */
    @SuppressWarnings("unchecked")
    @Before("cut()")
    public void before(JoinPoint joinPoint) throws VanySoftAspectException {

        // 获取当前url的值
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        HttpServletResponse response = sra.getResponse();
        String url = request.getServletPath();
        log.info("校验权限url：" + url);

        // 查询当前url的访问级别
        // url的访问级别，权限控制的力度。OPEN：完全开放；LOGIN：需要登录才能访问；AUTHORIZED：被授权的用户才能访问；ADMINSTRATORS：系统管理员角色才能访问
        String authorizedSocpe;
        List<Api> apis = (List<Api>) redisService.get(RedisConstants.API_LIST);
        // redis缓存中未查询api缓存，终止访问
        if (null == apis) {
            throw new VanySoftBaseException(MessageCodeConstant.AOPConstant.AOP_NO_BASEAPILIST);
        }

        Api api = apis.stream().filter(a -> Objects.equals(a.getUri(), url)).findFirst().orElse(null);

        //不允许浏览器端或缓存服务器缓存当前页面信息。
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);
        response.addHeader("Cache-Control", "no-cache");//浏览器和缓存服务器都不应该缓存页面信息
        response.addHeader("Cache-Control", "no-store");//请求和响应的信息都不应该被存储在对方的磁盘系统中；
        response.addHeader("Cache-Control", "must-revalidate");//于客户机的每次请求，代理服务器必须想服务器验证缓存是否过时；
        if (null == api) {
            log.error("url[" + url + "]未注册到sys_api表中，请注册！");
            throw new VanySoftBaseException(MessageCodeConstant.AOPConstant.AOP_URI_NO_RECORD);
        }
        if (StringUtils.isNotEmpty(api.getAuthCode())) {
            authorizedSocpe = api.getAuthCode();
        } else {
            log.error("url[" + url + "]非法注册，请重新注册");
            throw new VanySoftBaseException(MessageCodeConstant.AOPConstant.AOP_URI_NO_RECORD);
        }
        switch (authorizedSocpe) {
            // 当前url为全开发的url，不做拦截
            case BASE_API_OPEN:
                return;
            // url需要验证token是否过期
            case BASE_API_LOGIN:
                // 验证token是否过期
                checkToken(request);
                return;
            // url需要重新处理代码
            case BASE_API_AUTHORIZED:
                // 验证当前用户是否有url访问权限
                checkUrlPermission(request, url);
                return;
            case BASE_API_ADMINSTRATORS:
                //  验证当前用户是否有系统管理员角色
                checkAdministrator(request);
                return;
            default:
                log.error("url[" + url + "]非法注册，请重新注册");
                throw new VanySoftBaseException(MessageCodeConstant.AOPConstant.AOP_URI_NO_RECORD);
        }
    }

    /**
     * Function Name               checkToken
     *
     * @param request
     * @return
     * @description 验证登录是否过期
     * Modify History:              Date             Programmer       Notes
     * ---------        ---------------  ---------
     * 2017年7月3日 下午5:07:59           SharpClaw_x                     Initial
     * *********************************************************************
     */
    private UserInfo checkToken(HttpServletRequest request) {
        // 验证request中是否有token参数，否则异常
        String token = request.getParameter("token");
        if (StringUtils.isEmpty(token)) {
            throw new VanySoftAspectException(MessageCodeConstant.PermissionConstant.SESSIONERROR);
        }

        // 验证redis对应的token对应的用户是否有信息，否则异常
        UserInfo userInfo = redisService.getUserInfo(token);
        if (null == userInfo) {
            throw new VanySoftAspectException(MessageCodeConstant.PermissionConstant.SESSIONERROR);
        }

        return userInfo;
    }

    /**
     * Function Name               checkUrlPermission
     *
     * @param request
     * @param url
     * @description 验证当前用户拥有此url权限
     * Modify History:              Date             Programmer       Notes
     * ---------        ---------------  ---------
     * 2017年7月3日 下午5:06:43           SharpClaw_x                     Initial
     * *********************************************************************
     */
    private void checkUrlPermission(HttpServletRequest request, String url) {
        // 验证token时候过期
        UserInfo userInfo = checkToken(request);

        // 如果当前用户为系统管理员，则跳过权限验证
        // 验证当前用户的角色
        if (userInfo.isAdmin()) {
            return;
        }

        // 验证redis中对应的用户信息是否包含当前请求的操作权限，否则异常
        List<String> permissionList = userInfo.getPermissions();
        if (!permissionList.contains(url)) {
            throw new VanySoftAspectException(MessageCodeConstant.PermissionConstant.USER_NOPERMISSION);
        }

    }

    /**
     * Function Name               checkAdministrator
     *
     * @param request
     * @description 验证当前用户是否有系统管理员角色
     * Modify History:              Date             Programmer       Notes
     * ---------        ---------------  ---------
     * 2017年7月3日 下午5:06:14           SharpClaw_x                     Initial
     * *********************************************************************
     */
    private void checkAdministrator(HttpServletRequest request) {
        // 验证token时候过期
        UserInfo baseUserInfo = checkToken(request);

        // 验证当前用户的角色
        if (!baseUserInfo.isAdmin()) {
            log.error("当前url只能由系统管理员访问！");
            throw new VanySoftAspectException(MessageCodeConstant.PermissionConstant.USER_NOPERMISSION);
        }
    }

}
